#include <cassert>
#include <cmath>
#include <istream>
#include <vector>
#include <map>

#include <boost/noncopyable.hpp>

#include <kuai/misc/typedef.hpp>
#include <kuai/misc/xyz.hpp>
#include <kuai/misc/constant.hpp>
#include <kuai/mol/pbc.hpp>
#include <kuai/mol/molalgo.hpp>



#ifndef _KUAI_MOL_H_
#define _KUAI_MOL_H_

namespace kuai {

	class Element {
	public:
		Index		number;
		String		symbol;
		RealNumber	weight;
		Index		implicitH;
		Index		commonValence;
		Index		maxValence;
		RealNumber	valenceRadii;
		RealNumber	ionPotential;
		RealNumber	elecAffinities;
		

		static const Element& getElement(Index i);
		static const Element& getElement(const Char symbol[]);
		static const Element& getElement(const String& symbol);

		static void update(std::istream& stream);
		static void update(const Char filename[]);
		static void update();
	private:
		static std::vector<Element> elements;
		static std::map<String, Index> sym2element;
	};

	class Atom
	{ 
	public:
		Index type;

		bool operator==(const Atom& v2) const {
			return type == v2.type;
		}
		bool operator!=(const Atom& v2) const {
			return type != v2.type;
		}
		const Element& element() const {
			return Element::getElement(type); 
		}
	};

	/// return whether the symbol is "C"
	inline bool
		isCarbon(
		const Atom& v0
		) throw()
	{
		return v0.type == 6;
	}

	/// return whether the symbol is "N"
	inline bool
		isNitrogen(
		const Atom& v0
		) throw()
	{
		return v0.type == 7;
	}

	/// return whether the symbol is "H"
	inline bool
		isHydrogen(
		const Atom& v0
		) throw()
	{
		return v0.type == 1;
	}

	/// return whether the symbol is "O"
	inline bool
		isOxygen(
		const Atom& v0
		) throw()
	{
		return v0.type == 8;
	}

	/// return whether the symbol is "Cl"
	inline bool
		isChlorine(
		const Atom& v0
		) throw()
	{
		return v0.type == 17;
	}

	/// return whether the symbol is "S"
	inline bool
		isSulphur(
		const Atom& v0
		) throw()
	{
		return v0.type == 16;
	}

	inline bool
		isFluorin(
		const Atom& v0
		) throw()
	{
		return v0.type == 9;
	}

	class Bond 
	{ 
	public:
		enum BondOrder {
			SINGLE_BOND=2,
			DOUBLE_BOND=4,
			TRIPLE_BOND=6,
			PARTIAL_BOND=3
		};

	public:
		Index order;

		bool operator==(const Bond& v2) const {
			return order == v2.order;
		}
		bool operator!=(const Bond& v2) const {
			return order != v2.order;
		}
	};

	void guessBondLink(Index nAtoms, const Index atoms[], const XYZ coords[], const PBC* pbc, std::vector<Index>& bonds, std::map<Index, RealNumber>& valenceRadii, RealNumber tor=0.2f);

	
	RealNumber formulaWeight(const String& formula);

} // namespace kuai

#endif // _KUAI_MOL_H_
